﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

namespace CoolCode
{
	[AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
	public class AttachDataAttribute : Attribute
	{
		public AttachDataAttribute(object key, object value)
		{
			this.Key = key;
			this.Value = value;
		}

		public object Key { get; private set; }

		public object Value { get; private set; }
	}

	public static class AttachDataExtensions
	{
		private static Dictionary<string, object> _cacheAttachedData = new Dictionary<string, object>();

		public static object GetAttachedData(
			this ICustomAttributeProvider provider, object key)
		{
			var attributes = (AttachDataAttribute[])provider.GetCustomAttributes(
				typeof(AttachDataAttribute), false);
			return attributes.First(a => a.Key.Equals(key)).Value;
		}

		public static T GetAttachedData<T>(
			this ICustomAttributeProvider provider, object key)
		{
			return (T)provider.GetAttachedData(key);
		}

		public static object GetAttachedData(this Enum en, object key)
		{
			object value;
			string enumFullName = getEnumFullName(en, key);
			if (!_cacheAttachedData.TryGetValue(enumFullName, out value))
			{
				value = en.GetType().GetField(en.ToString()).GetAttachedData(key);
				_cacheAttachedData.Add(enumFullName, value);
			}
			return value;
		}

		public static T GetAttachedData<T>(this Enum en, object key)
		{
			return (T)en.GetAttachedData(key);
		}

		private static string getEnumFullName(Enum en, object key)
		{
			return en.GetFullName() + "." + key;
		}
	}

	/*Sample:
public enum AgeRange
{ 
	[AttachData(AgeRangeAttachData.Text, "18岁及以下")]
	LessThan18,

	[AttachData(AgeRangeAttachData.Text, "19至29岁")]
	From19To29,

	[AttachData(AgeRangeAttachData.Text, "30岁及以上")]
	Above29
}

public enum AgeRangeAttachData
{ 
	Text
}

public static class AgeRangeExtensions
{
	public static string GetText(this AgeRange range)
	{
		return range.GetAttachedData<string>(AgeRangeAttachData.Text);
	}
}

	 */

	[AttributeUsage(AttributeTargets.Field, AllowMultiple = false)]
	public class AttachStringAttribute : Attribute
	{
		public string Value { get; private set; }

		public AttachStringAttribute(string value)
		{
			this.Value = value;
		}
	}

	public static class AttachStringExtensions
	{
		private static Dictionary<string, string> _cacheAttachedData = new Dictionary<string, string>();

		/// <summary>
		/// 获取Enum中标记[AttachString]的字符串
		/// </summary>
		/// <param name="en"></param>
		/// <returns></returns>
		public static string GetAttachedString<T>(this T en)
		{
			string value;
			string enumFullName = en.GetFullName();
			if (!_cacheAttachedData.TryGetValue(enumFullName, out value))
			{
				value = getAttachedStringHelper(en);
				_cacheAttachedData.Add(enumFullName, value);
			}
			return value;
		}

		private static string getAttachedStringFromAttribute(this ICustomAttributeProvider provider)
		{
			var attributes = (AttachStringAttribute[])provider.GetCustomAttributes(
				typeof(AttachStringAttribute), false);
			if (attributes == null || attributes.Length == 0)
				throw new NotSupportedException("e.g. [AttachString(\"foo\")]");
			return attributes.First().Value;
		}

		private static string getAttachedStringHelper<T>(this T value)
		{
			return value.GetType().GetField(value.ToString()).getAttachedStringFromAttribute();
		}


	}

	internal static class EnumHelper
	{
		public static string GetFullName(this Enum en)
		{
			return en.GetType().FullName + "." + en.ToString();
		}

		public static string GetFullName<T>(this T en)
		{
			return en.GetType().FullName + "." + en.ToString();
		}
	}

	public class Enum<T> //where T:Enum
	{
		public static void Foreach(Action<T> action)
		{
			Array values = Enum.GetValues(typeof(T));
			foreach (var value in values)
			{
				action((T)value);
			}
		}

		public static T FindByAttachedString(string name)
		{
			return Find(c => c.GetAttachedString() == name);
		}

		public static T Find(Func<T, bool> func)
		{
			Array values = Enum.GetValues(typeof(T));
			foreach (var value in values)
			{
				T t = (T)value;
				if (func(t))
					return t;
			}
			return default(T);
		}

		public static List<T> ToList()
		{
			Array values = Enum.GetValues(typeof(T));
			List<T> list = new List<T>(values.Length);
			foreach (var value in values)
			{
				list.Add((T)value);
			}
			return list;
		}

		public static IEnumerable<T> AsEnumerable()
		{
			//Array values = Enum.GetValues(typeof(T));
			EnumQuery<T> query = new EnumQuery<T>();
			return query;
		}

		public static IEnumerable<TResult> Select<TResult>(Func<T, TResult> selector)
		{
			return ToList().Select(selector);
		}

		public static IEnumerable<T> Where(Func<T, bool> predicate)
		{
			return AsEnumerable().Where(predicate);
		}
	}

	public class EnumQuery<T> : IEnumerable<T>
	{
		private List<T> list;
		#region IEnumerable<T> Members

		public IEnumerator<T> GetEnumerator()
		{
			Array values = Enum.GetValues(typeof(T));
			list = new List<T>(values.Length);
			foreach (var value in values)
			{
				list.Add((T)value);
			}
			return list.GetEnumerator();
		}

		#endregion

		#region IEnumerable Members

		System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
		{
			return GetEnumerator();
		}

		#endregion


	}
}
